/*
 * Simple test file for cxxomfort
 * When compiled and run, this will print information about the
 * C++ and cxxomfort install.
 *
 * 0info.cpp is a much simpler version that only
 * outputs the information of cxxomfort's info object.
 *
 * This and other examples assume cxxomfort is installed in
 * include library path; if otherwise, set eg.: -I flag accordingly.
 */
//#define CXXOMFORT_NOTICES 2
#include <cxxomfort/base.hpp>
#include <cxxomfort/backports.hpp>
#include <cxxomfort/library/type_name.hpp>
//#include <cxxomfort/library.hpp>
#include <iterator>
#include <iostream>
#include <iomanip>
#include <list>

void show_support (unsigned v, const char* const text, std::ostream& os= std::cout);
void show_implementation (char const * const name, unsigned p, std::ostream& os= std::cout);
void report_integrals ();
void report_endianness ();
void report_tr1 ();
void show_impls_cxx11 (std::ostream&);
void show_impls_cxx14 (std::ostream&);
void show_impls_cxx17 (std::ostream&);
void show_impls_algorithm (std::ostream&);
void show_impls_string (std::ostream&);
void show_impls_string_view (std::ostream&);
void show_impls_tuple (std::ostream&);
void show_impls_alignment (std::ostream& os);


bool is_even (int x) { return x % 2 == 0; }
int int_transform (int x) { return x+1; }


typedef std::array<int,5> Arr;
static Arr aexpl = {{2, 3, 5, 7, 11}};

int main () {
    using namespace std;

    cxxomfort::output_info();
    cout<< endl;

    report_integrals();
    report_endianness();

    cout<< "Found supported by the compiler:"<< endl;
    cout<< "\nc++03\n";
    show_support(CXXO_COMPILER_SUPPORT_functional_oldstyle, "cxx03-oldstyle-objects");
    cout<< endl;
    report_tr1();
#if defined(_POSIX_VERSION)
    cout<< "POSIX: "<< _POSIX_VERSION<< endl;
#endif

/*

    show_header(CXXO_CXXSTDHEADER_array, "<array>");
    show_support(CXXO_CXXSTDHEADER_cstdint, "<cstdint>");
    show_support(CXXO_CXXSTDHEADER_cinttypes, "<cinttypes>");
    show_support(CXXO_CXXSTDHEADER_cuchar, "<cuchar>");
    show_support(CXXO_CXXSTDHEADER_ratio, "<ratio>");
    show_support(CXXO_CXXSTDHEADER_tuple, "<tuple>");
    show_support(CXXO_CXXSTDHEADER_type_traits, "<type_traits>");
    show_support(CXXO_CXXSTDHEADER_system_error, "<system_error>");
*/


    cout<< "\n\nc++11\n";
    show_support(CXXO_COMPILER_SUPPORT_initializer_list, "initializer_list");
    show_support(CXXO_COMPILER_SUPPORT_alignof, "alignof()");
    show_support(CXXO_COMPILER_SUPPORT_alignment_tools, "aligned_...");
    show_support(CXXO_COMPILER_SUPPORT_attribute, "[[attribute]]");
    show_support(CXXO_COMPILER_SUPPORT_auto, "c++11 auto");
    show_support(CXXO_COMPILER_SUPPORT_common_type, "common_type");
    show_support(CXXO_COMPILER_SUPPORT_conditional_enableif, "enable_if/conditional");
    show_support(CXXO_COMPILER_SUPPORT_constexpr, "constexpr");
    show_support(CXXO_COMPILER_SUPPORT_constexpr_dereference, "constexpr dereference");
    show_support(CXXO_COMPILER_SUPPORT_decltype, "decltype()");
    show_support(CXXO_COMPILER_SUPPORT_default_delete, "= default/delete");
    show_support(CXXO_COMPILER_SUPPORT_enum_class, "enum class");
    show_support(CXXO_COMPILER_SUPPORT_explicit_operator, "T::explicit operator");
    show_support(CXXO_COMPILER_SUPPORT_foreach, "foreach(elem : range)");
    show_support(CXXO_COMPILER_SUPPORT_inheriting_constructors, "inheriting constructors");
    show_support(CXXO_COMPILER_SUPPORT_local_types, "local_types_in_templates");
    show_support(CXXO_COMPILER_SUPPORT_noexcept, "noexcept(...)");
    show_support(CXXO_COMPILER_SUPPORT_nullptr, "nullptr");
    show_support(CXXO_COMPILER_SUPPORT_rvref, "T&&");
    show_support(CXXO_COMPILER_SUPPORT_static_assert, "static_assert()");
    show_support(CXXO_COMPILER_SUPPORT_std_is_constructible_traits, "is_constructible");
    show_support(CXXO_COMPILER_SUPPORT_std_is_trivially, "is_trivially...");
    show_support(CXXO_COMPILER_SUPPORT_iterator_accessors_11, "iterator_helpers");
    show_support(CXXO_COMPILER_SUPPORT_udl_s_basic_string, "\"\"s basic_string");
    show_support(CXXO_COMPILER_SUPPORT_unique_ptr, "unique_ptr");
    show_support(CXXO_COMPILER_SUPPORT_user_defined_literals, "user defined literals");
    show_support(CXXO_COMPILER_SUPPORT_variadic, "variadic_templates");

    cout<< "\n\nc++14\n";
    show_support(CXXO_COMPILER_SUPPORT_attribute_deprecated, "attribute[[deprecated]]");
    show_support(CXXO_COMPILER_SUPPORT_constexpr_relaxed, "constexpr_relaxed");
    show_support(CXXO_COMPILER_SUPPORT_decltypeauto, "decltype(auto)");
    show_support(CXXO_COMPILER_SUPPORT_transparent_operators_functional, "transparent plus<> etc");
    show_support(CXXO_COMPILER_SUPPORT_integer_sequence, "integer_sequence<>");
    show_support(CXXO_COMPILER_SUPPORT_nsdmi, "NSDMI");
    show_support(CXXO_COMPILER_SUPPORT_trailing_returns, "() -> return_type");

    cout<< "\n\nc++17\n";
    show_support(CXXO_CXXSTDHEADER_any, "<any");
    show_support(CXXO_CXXSTDHEADER_optional, "<optional>");
    show_support(CXXO_CXXSTDHEADER_string_view, "<string_view>");
    show_support(CXXO_CXXSTDHEADER_variant, "<variant>");
    show_support(CXXO_COMPILER_SUPPORT_bool_constant, "bool_constant<B>");
    show_support(CXXO_COMPILER_SUPPORT_std_byte, "byte");
    show_support(CXXO_COMPILER_SUPPORT_invoke, "invoke(f, ...)");
    show_support(CXXO_COMPILER_SUPPORT_udl_sv_basic_string_view, "\"\"sv basic_string_view");

    cout<< "\n\nc++20\n";
    show_support(CXXO_CXXSTDHEADER_endian, "<endian>");
    show_support(CXXO_COMPILER_SUPPORT_bit_cast, "bit_cast<>");
    //show_support(CXXO_COMPILER_SUPPORT_std_span, "std::span");

    cout<< "\nOther:\n";
    show_support(CXXO_COMPILER_SUPPORT_va_args  , "variadic_macros");
    show_support(CXXO_COMPILER_SUPPORT_typeof, "__typeof__()");
    show_support(CXXO_COMPILER_SUPPORT_type_intrinsics, "__is_type_intrinsics()");
    cout<< "\n"<< endl;


    cout<< "\nFeatures of cxxomfort: "<< endl;
    cout<< "\n-- Keywords and Properties --"<< endl;
    show_implementation("[[attribute]]", CXXOMFORT_IMPLEMENTS_attribute);
    show_implementation("CXXO_TYPEOF", CXXOMFORT_IMPLEMENTS_typeof);
    show_implementation("CXXO_DECLTYPE", CXXOMFORT_IMPLEMENTS_decltype);
    show_implementation("CXXO_EXPLICIT_OPERATOR", CXXOMFORT_IMPLEMENTS_explicit_operator);
    show_implementation("CXXO_FOREACH", CXXOMFORT_IMPLEMENTS_foreach);
    show_implementation("integer typedefs", CXXOMFORT_IMPLEMENTS_cstdint_integrals);
    show_implementation("cxxomfort:nullptr", CXXOMFORT_IMPLEMENTS_nullptr);
    show_implementation("cxxomfort:static_assert", CXXOMFORT_IMPLEMENTS_static_assert);

    cout<< "\n-- Types and Functions --"<< endl;

    //print_impls_cxx11(cout);
    //print_impls_cxx14(cout);
    //print_impls_cxx17(cout);
    //print_impls_cxx20(cout);

    show_impls_algorithm(cout);

    cout<< "byte (std::byte): "<< endl;
    show_implementation("std::byte", CXXOMFORT_IMPLEMENTS_byte);
    //show_impl_byte();
    show_implementation("character types C++11", CXXOMFORT_IMPLEMENTS_charn_types);
    #if (defined(__cpp_unicode_character_types))
    {
    u16string u1;
    cout<< "  char16_t is: "<< cxxomfort::type_name<char16_t>()<< endl;
    }
    #endif
    cout<< "callable utilities:"<< endl;
    show_implementation("  mem_fn", CXXOMFORT_IMPLEMENTS_mem_fn);
    show_implementation("  invoke", CXXOMFORT_IMPLEMENTS_invoke);
	#if (CXXOMFORT_IMPLEMENTS_invoke>0)
    {
        cout<< "  invoke printf: ";
        invoke(printf, "Hola %s\n", "mundo");
    }
	#endif
    show_implementation("  invoke_r", CXXOMFORT_IMPLEMENTS_invoke_r);
	#if (CXXOMFORT_IMPLEMENTS_invoke_r>0)
    {
        cout<< "  invoke_r printf: ";
        invoke_r<int>(printf, "Hola %s\n", "mundo");
    }
	#endif
    show_implementation("  bind_front",  CXXOMFORT_IMPLEMENTS_bind_front);
#if (CXXOMFORT_IMPLEMENTS_bind_front>0 && CXXO_COMPILER_SUPPORT_auto)
    {
        cout<< "  bind_front test: ";
        auto fnb = bind_front(printf, "Hola %s\n");
        fnb("mundo");
    }
#endif

    show_implementation("bit_cast", CXXOMFORT_IMPLEMENTS_bit_cast);
    show_implementation("destroy_at", CXXOMFORT_IMPLEMENTS_destroy_at);

    //show_implementation("[enable_if/conditional]", CXXOMFORT_IMPLEMENTS_enableif);
    show_implementation("endian", CXXOMFORT_IMPLEMENTS_endian);
    {
        cout<< "  endianness \t: "<< "native="<< (unsigned)endian::native<< " big="<< (unsigned)endian::big<< " little="<< (unsigned)endian::little<< endl;
    }

    show_implementation("heterogeneous <functional>", CXXOMFORT_IMPLEMENTS_heterogeneous_operator_functors);
    show_implementation("integer_sequence", CXXOMFORT_IMPLEMENTS_integer_sequence);

    cout<< "Iterator accessors:"<< endl;
    show_implementation("C++11: begin, end               ", CXXOMFORT_IMPLEMENTS_iterator_accessors_11);
    show_implementation("C++14: cbegin, rbegin, etc      ", CXXOMFORT_IMPLEMENTS_iterator_accessors_14);
    show_implementation("C++17: size, data, etc          ", CXXOMFORT_IMPLEMENTS_iterator_accessors_17);
    //show_implementation("ssize(Sequence)", CXXO_IMPLEMENTS_ssize);


    show_impls_string(cout);
    cout<< endl;
    show_impls_string_view(cout);
    cout<< endl;

    show_implementation("<system_error> utilities", CXXOMFORT_IMPLEMENTS_header_system_error);
    //show_implementation("  POSIX 2008 errc", CXXOMFORT_IMPLEMENTS_posix2008_errc);
    show_implementation("  hash<std::error_code>", 0);
    {
        hash<std::error_code> H; (void)H;
    }

    show_implementation("timespec_get", CXXOMFORT_IMPLEMENTS_timespec_get);

    show_implementation("tuple<...>", CXXOMFORT_IMPLEMENTS_tuple);
    show_implementation("  get<type>(Tuple)", CXXOMFORT_IMPLEMENTS_tuple_get_type);
    #if (CXXOMFORT_IMPLEMENTS_tuple_get_type>0)
    {
        std::string str1 = make_from_tuple<std::string>(std::make_tuple(12, '*'));
        std::tuple<std::string, int, float> tu1 ("text", 2, -1.1f);
        cout<< "  assert on get<int> of tuple"<< endl;
        assert(std::get<int>(tu1)==2);
    }
    #endif
    show_implementation("  make_from_tuple", CXXOMFORT_IMPLEMENTS_make_from_tuple);
    #if (CXXOMFORT_IMPLEMENTS_make_from_tuple>0)
    {
        std::string str1 = make_from_tuple<std::string>(std::make_tuple(12, '*'));
        cout<< "  make_from_tuple -> string: "<< str1<< endl;
    }
    #endif
    show_implementation("  tuple_apply", CXXOMFORT_IMPLEMENTS_tuple_apply);
    #if (CXXOMFORT_IMPLEMENTS_tuple_apply>0)
    {
    }
    #endif

    cout<< "type traits and related utilities:"<< endl;
    show_implementation("  integral_constant", CXXOMFORT_IMPLEMENTS_integral_constant);
    show_implementation("    callable() ", CXXOMFORT_IMPLEMENTS_integral_constant_callable);
    show_implementation("  bool_constant", CXXOMFORT_IMPLEMENTS_bool_constant);
    #if (CXXOMFORT_IMPLEMENTS_bool_constant>0)
    {
		std::bool_constant<false> f;
		cout<< "  false: "<< f.value<< endl;
	}
    #endif
    show_implementation("  common_type<Args...>" , CXXOMFORT_IMPLEMENTS_common_type);
    show_implementation("  underlying_type<Enum>" , CXXOMFORT_IMPLEMENTS_underlying_type);
    #if (CXXOMFORT_IMPLEMENTS_underlying_type>0)
    {
        enum test1 { val1 = 11, };
        typedef std::underlying_type<test1>::type U;
        cout<< "  underlying test name: "<< cxxomfort::type_name< U >()<< endl;
    }
    #endif
    show_implementation("make_void<...> " , CXXOMFORT_IMPLEMENTS_make_void);
    show_implementation("using void_t= " , CXXOMFORT_IMPLEMENTS_std_void_t);

    show_implementation("unique_ptr", CXXOMFORT_IMPLEMENTS_unique_ptr);
    show_implementation("  hash<>", 0);
    show_implementation("  make_unique", CXXOMFORT_IMPLEMENTS_make_unique);
    #if (CXXOMFORT_IMPLEMENTS_make_unique>0)
    {
        unique_ptr<string> us = make_unique<string>("Hola Mundo");
        cout<< "  make_unique test *ptr: "<< *us<< endl;
    }
    #endif

	cout<< "<utility> and related utilities:"<< endl;
	show_implementation("  exchange function", CXXOMFORT_IMPLEMENTS_exchange);
	#if (CXXOMFORT_IMPLEMENTS_exchange>0)
	{
		int exA= 4, exB= 7;
		printf("  exchange values %d %d ", exA, exB);
		std::exchange(exA, exB);
		printf(" -> %d %d \n", exA, exB);
		assert(exA==7);
	}
	#endif
	cout<< endl;

    //show_implementation("void_t transformation", CXXOMFORT_IMPLEMENTS_n3911);
#if 0
    cout<< "\n-- Library Supplements --"<< endl;
    show_implementation("array_ref", CXXOMFORT_IMPLEMENTS_array_ref);
    show_implementation("fixed_vector", CXXOMFORT_IMPLEMENTS_fixed_vector);
    show_implementation("function_ref", CXXOMFORT_IMPLEMENTS_function_ref);
    show_implementation("tuple operations", CXXOMFORT_IMPLEMENTS_tuple_operations);
    //show_implementation("hexdump/hexload", CXXOMFORT_IMPLEMENTS_hex);
    //show_implementation("is_scoped_enum<T>", CXXO_IMPLEMENTS_is_scoped_enum);
#endif
    cout<< endl;

    bool_constant<false> F_;
    (void)F_;

    show_impls_alignment(cout);
    cout<< endl;

}

void show_header (unsigned v, std::string_view text, std::ostream& os) {
    using namespace std;
    if (v) {
	os<< text<< " ["<< v<< "]"<< " \t";
    }
}

void show_support (unsigned v, const char* const text, std::ostream& os) {
    using namespace std;
    if (v) {
        os<< text<< " \t["<< v<< "]"<< endl;
    } else {
    }
}

void show_implementation (char const* const name, unsigned p, std::ostream& os) {
    using namespace std;
#if (CXXOMFORT_CXX_STD >= 1997)
    os<< setw(24)<< left<< name<< "   \t: ";
#else
    os<< name<< "  \t: ";
#endif
    if (p == CXXO_IMPLSTATUS_LIBRARY() ) {
        os<< "supplemental";
    } else if (p == CXXO_IMPLSTATUS_NATIVE() ) {
        os<< "c++-native";
    } else if (p > CXXO_IMPLSTATUS_BACKPORT() ) {
        os<< "backport L"<< (p-CXXO_IMPLSTATUS_BACKPORT());
    } else if (p == CXXO_IMPLSTATUS_BACKPORT() ) {
        os<< "backport";
    } else if (p >= CXXO_IMPLSTATUS_EMULATION() ) {
        os<< "emulation L"<< (p-CXXO_IMPLSTATUS_EMULATION());
    } else if (p != 0) {
        os<< "partial status";
    } else {
        os<< "unavail/unknown";
    }
    os<< endl;
}

void report_tr1 () {
    using namespace std;
    cout<< "TR1: "<< CXXOFLAG_tr1
        << " dir:"<< CXXOFLAG_directory_tr1<< " ns:"<< CXXOFLAG_namespace_tr1
        << " "<< CXXO_STRINGIZE(CXXO_STDTR1_NAME())<< endl;
}

void report_integrals () {
    using namespace std;
    static const char* names[]= {"char", "short", "int", "long", "llong" };
    unsigned sizes[]= {sizeof(char), sizeof(short), sizeof(int), sizeof(long), sizeof(long long) };
    cout<< "sizes of integrals: "<< flush;
    for(unsigned i=0; i < 5 /*size(sizes)*/ ; ++i) {
        cout<< names[i]<< ':'<< sizes[i]<< " ";
    }
    cout<< endl;
}

void report_endianness () {
#if (CXXOMFORT_IMPLEMENTS_endian > CXXO_IMPLSTATUS_EMULATION())
    using namespace std;
    cout<< "Machine is: ";
    if (endian::native == endian::little) { cout<< "little"; }
    else if (endian::native == endian::big) { cout<< "big"; }
    else cout<< "unknown";
    cout<< "-endian"<< endl;
#else
    std::cout<< "endian not implemented"<< std::endl;
#endif
}


void show_impls_cxx11 (std::ostream& os) {
    using namespace std;
    static int const LI [] = {1, 3, 5, 7, 9}; // primes, totally primes
    list<int> l (begin(LI), end(LI));

    none_of(begin(l), end(l), is_even);
    show_implementation("[all/any/none]_of algorithms", CXXOMFORT_IMPLEMENTS_n2569 /*CXXOMFORT_IMPLEMENTS_algorithm_anyall*/, os);
}

void show_impls_cxx14 (std::ostream& os) {
    (void)os;
    using namespace std;
}

void show_impls_cxx17 (std::ostream& os) {
    using namespace std;
    using cxxomfort::to_byte;
    std::byte by = to_byte('c');
    (void)by;
    show_implementation("byte", CXXOMFORT_IMPLEMENTS_byte, os);
}


void show_impls_algorithm (std::ostream& os) {
    using namespace std;
    show_implementation("any_ algorithms C++11", CXXOMFORT_IMPLEMENTS_algorithm_allanynoneof);
    #if (CXXOMFORT_IMPLEMENTS_algorithm_allanynoneof)
        os<< " testing call: "<< any_of(begin(aexpl),end(aexpl),is_even)<< endl;
    #endif

    show_implementation("minmax algorithms C++11", CXXOMFORT_IMPLEMENTS_algorithm_minmax);
    #if (CXXOMFORT_IMPLEMENTS_algorithm_minmax)
        os<< "  testing minmax: ";
        pair< Arr::iterator,Arr::iterator > pmm= minmax_element(begin(aexpl),end(aexpl));
        os<< *pmm.first<< " , "<< *pmm.second<< endl;
    #endif

    show_implementation("sort algorithms C++11", CXXOMFORT_IMPLEMENTS_algorithm_sorted);
#if (CXXOMFORT_IMPLEMENTS_algorithm_sorted)
    os<< "  test is_sorted: "<< is_sorted(begin(aexpl), end(aexpl))<< endl;
#endif

    show_implementation("equal/mismatch update C++14", CXXOMFORT_IMPLEMENTS_equal_mismatch_full_ranges);

    show_implementation("clamp C++17", CXXOMFORT_IMPLEMENTS_clamp);
    clamp(aexpl[1], aexpl[2], aexpl[3]);

    show_implementation("shift_ algorithms C++20", CXXOMFORT_IMPLEMENTS_algorithm_shift);
#if (CXXOMFORT_IMPLEMENTS_algorithm_shift)
    os<< "invoke shift.."<< endl;
    shift_left(begin(aexpl), end(aexpl), 1);
    shift_right(begin(aexpl), end(aexpl), 1);
#endif
}

void show_impls_string (std::ostream& os) {
    using namespace std;
    show_implementation("<string> utilities", CXXOMFORT_IMPLEMENTS_string, os);
    show_implementation("  hash<>", CXXOMFORT_IMPLEMENTS_stdhash_string, os);
    show_implementation("  s literal", CXXOMFORT_IMPLEMENTS_udl_s, os);
    { hash<wstring> H; wstring s(L"1"); size_t h= H(s); (void)h; }
}

void show_impls_string_view (std::ostream& os) {
    using namespace std;
    show_implementation("<string_view> utilities", CXXOMFORT_IMPLEMENTS_header_string_view, os);
    show_implementation("  basic_string_view", CXXOMFORT_IMPLEMENTS_string_view, os);
    show_implementation("  hash<>", CXXOMFORT_IMPLEMENTS_stdhash_string_view, os);
    show_implementation("  sv literal", CXXOMFORT_IMPLEMENTS_udl_sv, os);
    { hash<wstring_view> H; wstring_view s(L"1"); size_t h= H(s); (void)h; }
}

void show_impls_alignment (std::ostream& os) {
    using namespace std;
    cout<< "alignof support: "<< CXXO_COMPILER_SUPPORT_alignof<< endl;
    os<< "alignof(int): "<< alignof(int)<< endl;
    cout<< "alignment tools support: "<< CXXO_COMPILER_SUPPORT_alignment_tools<< endl;
    cout<< "aligned_storage support: "<< CXXOMFORT_IMPLEMENTS_aligned_storage<< endl;
    aligned_storage<12, 4>::type A;
    (void)A;
    //os<< "alignment_storage<12,4> : "<< sizeof(A)<< " "<< alignof(A)<< endl;
}

